#include "../Card.h"
#include "../Deck.h"
#include <assert.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;

Card _ace(clubs, ace,3);
Card _joker(hearts, joker,3);
Card _king(hearts, king, 3);
string card_name(Card* c)
{
  if(c == &_ace)   return "Ace      ";
  if(c == &_joker) return "Joker    ";
  if(c == &_king)  return "King     ";
                  return "Nonsense!";
}

int main(void)
{
  Deck deckA;
  /* Check drawing and adding functionality. */
  deckA.add(&_ace);
  deckA.add(&_joker);
  deckA.add(&_king);
  assert(deckA.size() == 3);
  vector<Card*> results(3);
  results[0] = deckA.draw();
  results[1] = deckA.draw();
  results[2] = deckA.draw();
  assert(deckA.size() == 0);
  cout << "Drew " << card_name(results[0]) << ", " << card_name(results[1])
       << ", and " << card_name(results[2]) << "." << endl;
  assert(results[0] == &_ace);
  assert(results[1] == &_joker);
  assert(results[2] == &_king);

  /* Make sure you can add decks together. */
  Deck deckB;
  deckA.add(&_ace);
  deckA.add(&_joker);
  deckB.add(&_king);
  deckB.add(&deckA);
  cout << "deckB has " << deckB.size() << " cards." << endl;
  assert(deckB.size() == 3);
  
  /* Make sure you can empty decks using size and draw. */
  while(deckA.size()) deckA.draw();
  while(deckB.size()) deckB.draw();
  assert(deckA.size() == 0);
  assert(deckB.size() == 0);

  /* Make sure you can access a deck like an array. */
  deckA.add(&_ace);
  deckA.add(&_joker);
  deckA.add(&_king);
  cout << "Cards are " << card_name(deckA.at(0)) << ", "
       << card_name(deckA.at(1)) << ", and " << card_name(deckA.at(2)) << "."
       << endl;
  assert(deckA.at(0) == &_ace  );
  assert(deckA.at(1) == &_joker);
  assert(deckA.at(2) == &_king );

  /* Test multiple drawing. */
  deckB = deckA.draw(2);
  assert(deckA.size() == 1);
  assert(deckA.at(0) == &_king);
  assert(deckB.size() == 2);
  assert(deckB.at(0) == &_ace);
  assert(deckB.at(1) == &_joker);
  
  /* Test transfer. */
  deckB.transfer(1, &deckA);
  assert(deckA.size() == 2);
  assert(deckA.at(0) == &_king);
  assert(deckA.at(1) == &_ace);
  assert(deckB.size() == 1);
  assert(deckB.at(0) == &_joker);

  deckA.transfer(2, &deckB);
  assert(deckA.size() == 0);
  assert(deckB.size() == 3);
  assert(deckB.at(0) == &_joker);
  assert(deckB.at(1) == &_king);
  assert(deckB.at(2) == &_ace);

  /* Test Shuffle, make sure cards can end up anywhere. */
  bool hits[3][3];
  for(int i = 0; i < 3; i++)
    for(int j = 0; j < 3; j++)
      hits[i][j] = false;
  for(int i = 0; i < 1000; i++)
  {
    /* Preserve deckB. */
    deckA = deckB;
    /* Shuffle. */
    deckA.shuffle();
    /* See where cards landed. */
    for(int j = 0; j < 3; j++)
    {
      if(deckA.at(j) == &_joker) hits[j][0] = true;
      if(deckA.at(j) == &_king ) hits[j][1] = true;
      if(deckA.at(j) == &_ace  ) hits[j][2] = true;
    }
  }
  /* Did each card get everywhere? */
  cout << "Where cards got after shuffle:" << endl;
  for(int i = 0; i < 3; i++)
  {
    for(int j = 0; j < 3; j++)
      cout << hits[i][j] << " ";
    cout << endl;
  }
  for(int i = 0; i < 3; i++)
    for(int j = 0; j < 3; j++)
      assert(hits[i][j] == true);

  /* Try replace. */
  deckB.replace(0, &_ace);
  deckB.replace(1, &_king);
  deckB.replace(2, &_joker);
  assert(deckB.at(0) == &_ace);
  assert(deckB.at(1) == &_king);
  assert(deckB.at(2) == &_joker);

  /* Try sort. */
  Card* J     = new Card(   clubs, joker,  5);
  Card* C_2   = new Card(   clubs,    _2,  6);
  Card* D_5   = new Card(diamonds,    _5,  7);
  Card* H_5   = new Card(  hearts,    _5,  8);
  Card* S_5   = new Card(  spades,    _5,  9);
  Card* H_J_1 = new Card(  hearts,  jack, 10);
  Card* H_J_2 = new Card(  hearts,  jack, 11);
  Card* S_K   = new Card(  spades,  king, 12);

  Deck deckC;
  deckC.add(H_J_1);
  deckC.add(  S_K);
  deckC.add(  D_5);
  deckC.add(    J);
  deckC.add(  H_5);
  deckC.add(  C_2);
  deckC.add(H_J_2);
  deckC.add(  S_5);

  #if 1
    deckC.sort();
    cout << "Sorted deck:";
    for(int i = 0; i < deckC.size(); ++i)
    {
      cout << deckC.at(i)->getRank() << " ";
    }
    cout << endl;
  #endif

  assert(deckC.at(0)->getRank() == joker);
  assert(deckC.at(1)->getRank() ==    _2);
  assert(deckC.at(2)->getRank() ==    _5);
  assert(deckC.at(3)->getRank() ==    _5);
  assert(deckC.at(4)->getRank() ==    _5);
  assert(deckC.at(5)->getRank() ==  jack);
  assert(deckC.at(6)->getRank() ==  jack);
  assert(deckC.at(7)->getRank() ==  king);


  cout << "Deck passed all tests." << endl;
}
